//=============================================================================
/**
 *
 *	@file		fld_sereby_eff.c
 *
 *	@brief		フィールドセレビィエフェクト
 *	@author		NozomuSaito
 *	@data		2009.03.26
 *
 *	モジュール名：FldSerebyEff_
 *
 */
//=============================================================================
#include "common.h"
#include "system/wipe.h"
#include "fieldsys.h"
#include "fieldobj.h"
#include "fld_motion_bl.h"

#include "fld_demo.naix"

#include "fld_sereby_eff_def.h"

#include "fld_sereby_eff.h"

#define EFF_ANM_NUM		(3)		//アニメデータの数

typedef struct
{
	u16 Rad;
	u8 EffEnd;
	u8 RevZoomEnd;

	FLD_MOTION_BL_DATA_PTR	Motion;
	FIELDSYS_WORK * fsys;

	NNSFndAllocator Allocator;
	FLD_3DOBJ_MDL	EffMdl;
	FLD_3DOBJ_ANM	EffAnm[EFF_ANM_NUM];
	FLD_3DOBJ		EffObj;

	int AddRad;
	u16 OrgRad;
	u16 Counter;
	u16 Frm;
	u16 OrgPersp;
	u16 DstPersp;
	u16 Seq;

	u8 AnmStart;
	u8 DrawFlg;
	u8 Wait;
	u8 dummy;

}SEREBY_EFF_WORK;

typedef struct
{
	int Rad;
	int Sync;
	u16 Persp;
}REV_ZOOM_PARAM;

//シーケンス
enum{
	SEQ_WHITE_OUT_FIRST,
	SEQ_EFF_LOAD,
	SEQ_WHITE_IN_WAIT,
	SEQ_EFF_START,
	SEQ_WHITE_OUT,
	SEQ_EFF_WAIT
};

static BOOL FldSerebyEffEvt(GMEVENT_CONTROL * event);

static void StartEff(SEREBY_EFF_WORK *work);
static BOOL CheckEndEff(SEREBY_EFF_WORK *work);
static void EffTcb(TCB_PTR tcb, void *wk);
static void SetRevZoom(SEREBY_EFF_WORK* work, const REV_ZOOM_PARAM *inParam);
static BOOL RevZoomFunc(SEREBY_EFF_WORK* work);
static void RevZoomTcb(TCB_PTR tcb, void *wk);
static BOOL CheckRevZoom(SEREBY_EFF_WORK *work);
static void Load3DData(SEREBY_EFF_WORK *work);
static void Del3DData(SEREBY_EFF_WORK *work);
static void SetAnmFrame(FLD_3DOBJ_ANM *anm, const u32 inFrame);
static BOOL AnimeNoLoop(FLD_3DOBJ_ANM *anm);


#ifdef PM_DEBUG
static void TestCamMove(SEREBY_EFF_WORK *work);
#endif	//PM_DEBUG

//--------------------------------------------------------------
/**
 * イベントコール関数
 * @param	fsys			フィールドシステムポインタ
 * @return	none
 */
//--------------------------------------------------------------
void FldSerebyEff_CallEvt(FIELDSYS_WORK * fsys)
{
	SEREBY_EFF_WORK *work = sys_AllocMemoryLo(HEAPID_WORLD, sizeof(SEREBY_EFF_WORK));
	MI_CpuClear8(work, sizeof(SEREBY_EFF_WORK));
	work->fsys = fsys;
	//イベント発行
	FieldEvent_Call(fsys->event, FldSerebyEffEvt, work);
}

//--------------------------------------------------------------
/**
 * イベント関数
 * @param	event	イベントポインタ
 * @return	BOOL	TRUEで終了
 */
//--------------------------------------------------------------
static BOOL FldSerebyEffEvt(GMEVENT_CONTROL * event)
{
	int *seq = FieldEvent_GetSeqWork(event);
	FIELDSYS_WORK * fsys = FieldEvent_GetFieldSysWork(event);
	SEREBY_EFF_WORK* work = FieldEvent_GetSpecialWork(event);

	switch(*seq){
	case SEQ_WHITE_OUT_FIRST:
		WIPE_SYS_Start(	WIPE_PATTERN_M, WIPE_TYPE_FADEOUT,
						WIPE_TYPE_FADEOUT, WIPE_FADE_WHITE, WHITE_OUT_FIRST_SYNC, 1, HEAPID_FIELD );
		(*seq)++;
		break;
	case SEQ_EFF_LOAD:
		//ホワイトアウト待ち
		if ( WIPE_SYS_EndCheck() ){
			//データロード
			Load3DData(work);
			//連れ歩き非表示
			PairPoke_StartVanish(PairPoke_GetFldObjPtr(work->fsys), 1);
			//ホワイトイン
			WIPE_SYS_Start(	WIPE_PATTERN_M, WIPE_TYPE_FADEIN,
						WIPE_TYPE_FADEOUT, WIPE_FADE_WHITE, WHITE_IN_SYNC, 1, HEAPID_FIELD );
			(*seq)++;
		}
		break;
	case SEQ_WHITE_IN_WAIT:
		if( WIPE_SYS_EndCheck() ){
			//スクリプトでコールしてる（はず）ため、描画プロックがフックされているはずなので、ここでコールして、連れ歩きを消す
			FieldOBJ_DrawProcCall( PairPoke_GetFldObjPtr(work->fsys) );
			//アニメ開始
			work->AnmStart = 1;
			(*seq)++;
		}
		break;
	case SEQ_EFF_START:
		work->Wait++;
		if (work->Wait >= WAIT_EFF_START){
			//エフェクト開始
			StartEff(work);
			work->Wait = 0;
			(*seq)++;
		}
		break;
	case SEQ_WHITE_OUT:
		work->Wait++;
		if ( work->Wait >= WAIT_LAST_WHITE_OUT_START ){
#if 0 //GF1001 修正 by iwasawa 090705
			WIPE_SYS_Start(	WIPE_PATTERN_M, WIPE_TYPE_FADEOUT,
						WIPE_TYPE_FADEOUT, WIPE_FADE_WHITE, WHITE_OUT_LAST_SYNC, 1, HEAPID_FIELD );
#else
			WIPE_SYS_Start(	WIPE_PATTERN_WMS, WIPE_TYPE_FADEOUT,
						WIPE_TYPE_FADEOUT, WIPE_FADE_WHITE, WHITE_OUT_LAST_SYNC, 1, HEAPID_FIELD );
#endif
			(*seq)++;
		}
		break;
	case SEQ_EFF_WAIT:
		if ( WIPE_SYS_EndCheck()&&CheckEndEff(work) ){
			//解放処理
			{
				Del3DData(work);
				FLDMotionBl_Delete(&work->Motion);	//ブラー終了
				sys_FreeMemoryEz(work);		//ワーク解放
			}
			return TRUE;
		}
	}

	//3Dアニメ
	if (work->AnmStart){
		AnimeNoLoop(work->EffAnm);
	}

	//3D描画
	if (work->DrawFlg){
		FLD_3DObjDraw( &work->EffObj );
	}
	return FALSE;
}

//--------------------------------------------------------------
/**
 * 演出開始
 * @param	work	ワークポインタ
 * @return	none
 */
//--------------------------------------------------------------
static void StartEff(SEREBY_EFF_WORK *work)
{
	work->Motion = FLDMotionBl_Init(MOTIONBL_EVA, MOTIONBL_EAB);
	//カメラ移動ＴＣＢ登録
	TCB_Add(EffTcb, work, 0);
	work->EffEnd = 0;
}

//--------------------------------------------------------------
/**
 * エフェクト終了チェック
 * @param	work	ワークポインタ
 * @return	BOOL	TRUEで終了
 */
//--------------------------------------------------------------
static BOOL CheckEndEff(SEREBY_EFF_WORK *work)
{
	if (work->EffEnd){
		return TRUE;
	}
	return FALSE;
}

//--------------------------------------------------------------
/**
 * エフェクトタスク
 * @param	tcb		TCB
 * @param	wk		ワークポインタ
 * @return	none
 */
//--------------------------------------------------------------
static void EffTcb(TCB_PTR tcb, void *wk)
{
	SEREBY_EFF_WORK *work = wk;

	switch(work->Seq){
	case 0:
		{
			REV_ZOOM_PARAM param = {REV_VAL, EFF_SYNC, EFF_PERSP};
			param.Persp = GFC_GetCameraPerspWay(work->fsys->camera_ptr);
			SetRevZoom(work, &param);
		}
		work->Seq++;
		break;
	case 1:
		if (CheckRevZoom(work)){
			work->EffEnd = 1;
			TCB_Delete(tcb);
		}
		break;
	}
/**
	TestCamMove(work);

	if (sys.trg & PAD_BUTTON_START){
		work->EffEnd = 1;
		TCB_Delete(tcb);
	}
*/
}

//--------------------------------------------------------------
/**
 * 回転、ズームセット関数
 * @param	work		ワークポインタ
 * @param	inParam		パラメータ
 * @return	none
 */
//--------------------------------------------------------------
static void SetRevZoom(SEREBY_EFF_WORK* work, const REV_ZOOM_PARAM *inParam)
{
	work->RevZoomEnd = 0;
	work->Counter = 0;
	//パラメータセット
	work->AddRad = inParam->Rad;
	work->OrgRad = work->Rad;
	work->OrgPersp = GFC_GetCameraPerspWay(work->fsys->camera_ptr);
	work->DstPersp = inParam->Persp;
	work->Frm = inParam->Sync;

	TCB_Add(RevZoomTcb, work, 1);
}

//--------------------------------------------------------------
/**
 * 回転計算
 * @param	work	ワークポインタ
 * @return	BOOL	TRUEで計算終了
 */
//--------------------------------------------------------------
static BOOL RevZoomFunc(SEREBY_EFF_WORK* work)
{
	int persp;
	int dif;
	int rad;
	u16 dst_rad;
	int org_rad;
	VecFx32 camUp;
	MtxFx33 mtx;
	VecFx32 vec = {0,FX32_ONE,0};
	GF_CAMERA_PTR camera_ptr = work->fsys->camera_ptr;

	work->Counter++;

	org_rad = work->OrgRad;
	rad = (  work->AddRad * work->Counter) / work->Frm;
	dst_rad = org_rad+rad;

	dif = work->DstPersp - work->OrgPersp;
	persp = ( dif * work->Counter) / work->Frm;
	persp = work->OrgPersp+persp;
	MTX_RotZ33(&mtx, FX_SinIdx(dst_rad), FX_CosIdx(dst_rad));
	MTX_MultVec33(&vec, &mtx, &camUp);
	GFC_SetCamUp(&camUp, camera_ptr);
	GFC_SetCameraPerspWay(persp, camera_ptr);

	if ( work->Counter >= work->Frm ){
		return TRUE;
	}
	return FALSE;
}

//--------------------------------------------------------------
/**
 * 回転、ズームタスク
 * @param	tcb		TCB
 * @param	wk		ワークポインタ
 * @return	none
 */
//--------------------------------------------------------------
static void RevZoomTcb(TCB_PTR tcb, void *wk)
{
	SEREBY_EFF_WORK *work = wk;
	if ( RevZoomFunc(work) ){
		work->RevZoomEnd = 1;
		TCB_Delete(tcb);
	}
}

//--------------------------------------------------------------
/**
 * 回転、ズーム終了チェック
 * @param	work	ワークポインタ
 * @return	BOOL	TRUEで終了
 */
//--------------------------------------------------------------
static BOOL CheckRevZoom(SEREBY_EFF_WORK *work)
{
	if (work->RevZoomEnd){
		return TRUE;
	}
	return FALSE;
}

//--------------------------------------------------------------
/**
 * 3Ｄデータロード関数
 * @param	work	ワークポインタ
 * @return	none
 */
//--------------------------------------------------------------
static void Load3DData(SEREBY_EFF_WORK *work)
{
	u8 i;
	int heap = HEAPID_FIELD;
	const int anm[EFF_ANM_NUM] = {
		NARC_fld_demo_sere_demo01_nsbca,
		NARC_fld_demo_sere_demo01_nsbtp,
		NARC_fld_demo_sere_demo01_nsbta,
	};

	// アロケーター作成
	sys_InitAllocator( &work->Allocator, heap, 32 );

	//モデルロード
	FLD_3DObjMdlLoad(&work->EffMdl,ARC_FLD_DEMO_GRA , NARC_fld_demo_sere_demo01_nsbmd, heap);
	//アニメデータロード
	for (i=0;i<EFF_ANM_NUM;i++){
		FLD_3DObjAnmLoad(&work->EffAnm[i], &work->EffMdl, ARC_FLD_DEMO_GRA, anm[i],
		heap, &work->Allocator );
	}

	//描画オブジェクト初期化
	FLD_3DObjInit( &work->EffObj, &work->EffMdl );
	//アニメをリンク
	for (i=0;i<EFF_ANM_NUM;i++){
		FLD_3DObjAddAnm( &work->EffObj, &work->EffAnm[i] );
	}
	//描画フラグ設定	(表示)
	FLD_3DObjSetDraw( &work->EffObj, TRUE );
	//フレームセット
	SetAnmFrame(work->EffAnm, 0);
	//表示位置セット
	{
		VecFx32 vec;
		//連れ歩き（セレビィ）の位置に表示
		FieldOBJ_VecPosGet( PairPoke_GetFldObjPtr(work->fsys), &vec );
		FLD_3DObjSetMatrix( &work->EffObj, vec.x, vec.y, vec.z );
	}

	//描画開始
	work->DrawFlg = 1;
}

//--------------------------------------------------------------
/**
 * 3Ｄデータ解放関数
 * @param	work	ワークポインタ
 * @return	none
 */
//--------------------------------------------------------------
static void Del3DData(SEREBY_EFF_WORK *work)
{
	u8 i;
	//アニメデータ破棄
	for (i=0;i<EFF_ANM_NUM;i++){
		FLD_3DObjAnmDelete( &work->EffAnm[i], &work->Allocator);
	}
	//モデルデータ破棄
	FLD_3DObjMdlDelete( &work->EffMdl);
}


//==============================================================================
/**
 * アニメフレームセット
 *
 * @param	anm			アニメポインタ配列
 * @param	inFrame		フレーム
 *
 * @retval  none
 */
//==============================================================================
static void SetAnmFrame(FLD_3DOBJ_ANM *anm, const u32 inFrame)
{
	u8 i;
	for (i=0;i<EFF_ANM_NUM;i++){
		FLD_3DObjAnmSet( &anm[i], inFrame  );
	}
}

//==============================================================================
/**
 * アニメフレーム更新関数
 *
 * @param	anm			アニメポインタ配列
 *
 * @retval  BOOL		TRUE：アニメ終了	FALSE：アニメ継続
 */
//==============================================================================
static BOOL AnimeNoLoop(FLD_3DOBJ_ANM *anm)
{
	u8 i;
	BOOL end;
	u8 end_num = 0;
	for (i=0;i<EFF_ANM_NUM;i++){
		end = FLD_3DObjAnmNoLoop( &anm[i], FX32_ONE );
		if (end){
			OS_Printf("%d:アニメ終了\n",i);
			end_num++;
		}
	}
	if (end_num == EFF_ANM_NUM){
		return TRUE;
	}

	return FALSE;
}


#ifdef PM_DEBUG

#define ANGLE_MOVE	(32*4)
static void TestCamMove(SEREBY_EFF_WORK *work)
{
	VecFx32 camUp;
	MtxFx33 mtx;
	VecFx32 vec = {0,FX32_ONE,0};
	GF_CAMERA_PTR camera_ptr = work->fsys->camera_ptr;

	if ( sys.trg & PAD_KEY_LEFT ){
		work->Rad += ANGLE_MOVE;
	}else if( sys.trg & PAD_KEY_RIGHT ){
		work->Rad -= ANGLE_MOVE;
	}else if ( sys.cont & PAD_KEY_UP ){
		work->Rad += ANGLE_MOVE;
	}else if( sys.cont & PAD_KEY_DOWN ){
		work->Rad -= ANGLE_MOVE;
	}else if ( sys.trg & PAD_BUTTON_SELECT ){
		work->Rad = 0;
	}

	MTX_RotZ33(&mtx, FX_SinIdx(work->Rad), FX_CosIdx(work->Rad));
	MTX_MultVec33(&vec, &mtx, &camUp);
	GFC_SetCamUp(&camUp, camera_ptr);
}

#endif	//PM_DEBUG